/**
 * WordNode is a node in a tree of words.
 * <br>
 * This source code is copyright 2008 by Patrick May.  All rights
 * reserved.
 *
 * @author Patrick May (patrick.may@mac.com)
 * @author &copy; 2008 Patrick May.  All rights reserved.
 * @version 1
 */

package com.game.ghost.webapp.apps.data;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.logging.Logger;

public class WordNode implements Serializable {

	/**
	 * serialVersionUID
	 */
	private static final long serialVersionUID = -2646955727829409152L;
	
	public static final String CACHE_NAME = "wordNode";

	private static Logger logger = Logger.getLogger(WordNode.class.getName());

	private String string = null;
	private HashMap<String, WordNode> childNodes = new HashMap<String, WordNode>();
	private Boolean forcedWin = null;
	private String longestLosingWord = null;

	/**
	 * Add a new terminal node to the tree.
	 * 
	 * @param node
	 *            The new terminal node.
	 */
	private void addTerminal(WordNode node) {
		if (node.string.length() == (string.length() + 1))
			childNodes.put(node.string, node);
		else {
			if(!node.string.equals(string)){
				String key = node.string.substring(0, string.length() + 1);
				WordNode child = childNodes.get(key);
				if (child == null) {
					child = new WordNode(key);
					childNodes.put(key, child);
				}
	
				child.addTerminal(node);
			}
		}
	}

	/**
	 * The full constructor for the WordNode class.
	 * 
	 * @param baseString
	 *            The string that starts all words in the tree rooted at this
	 *            node.
	 * @param iterator
	 *            An iterator that returns an alphabetical list of words all
	 *            starting with the same string.
	 */
	public WordNode(String baseString, ListIterator<String> iterator) {
		string = baseString;

		while (iterator.hasNext())
			addTerminal(new WordNode(iterator.next()));
	}

	/**
	 * The single word constructor for the WordNode class.
	 * 
	 * @param word
	 *            The base string for the node.
	 */
	public WordNode(String word) {
		string = word;
	}

	/**
	 * Return the number of child nodes.
	 */
	public long numberOfChildren() {
		return childNodes.size();
	}

	/**
	 * Return the child node associated with the specified string.
	 * 
	 * @param start
	 *            The string that starts all the words rooted at the requested
	 *            child node.
	 */
	public WordNode childNode(String start) {
		WordNode node = null;
		if (string.equals(start))
			node = this;
		else if ((string.length() < start.length())
				&& ((node = childNodes.get(start.substring(0,
						string.length() + 1))) != null))
			node = node.childNode(start);

		return node;
	}

	/**
	 * Return the string that is the base for this node and all child nodes.
	 */
	public String baseString() {
		return string;
	}

	/**
	 * Return the children of this node.
	 */
	public Collection<WordNode> childNodes() {
		return childNodes.values();
	}

	/**
	 * Return a boolean indicating whether or not this is a terminal node.
	 */
	public boolean isTerminalNode() {
		return childNodes.isEmpty();
	}

	/**
	 * Flag whether or not the node is the root of a forced win. Note that this
	 * is a flag reflecting the belief of the client of this class, not anything
	 * intrinsic in the state of an instance of WordNode.
	 * 
	 * @param forced
	 *            A boolean indicating whether or not a win is forced from this
	 *            node.
	 */
	public void flagForcedWin(boolean forced) {
		forcedWin = new Boolean(forced);
	}

	/**
	 * Return a boolean indicating whether or not this node has been flagged a
	 * forced win.
	 * 
	 * @return A Boolean indicating whether or not this node is the root of a
	 *         forced win. A return value of null means that the flag has not
	 *         been set.
	 */
	public Boolean isForcedWin() {
		return forcedWin;
	}

	/**
	 * Return the first terminal word found under this node that is a forced
	 * win.
	 */
	public String firstWinningWord() {
		String word = null;

		if (isTerminalNode() && isForcedWin())
			word = string;
		else {
			Iterator<String> keyIterator = childNodes.keySet().iterator();
			while ((word == null) && (keyIterator.hasNext()))
				word = childNodes.get(keyIterator.next()).firstWinningWord();
		}

		return word;
	}

	/**
	 * Return the longest losing word reachable from this node.
	 */
	public String longestLosingWord() {
		if (longestLosingWord == null) {
			longestLosingWord = string;

			Iterator<String> keyIterator = childNodes.keySet().iterator();
			WordNode child = null;
			while (keyIterator.hasNext()) {
				child = childNodes.get(keyIterator.next());
				if (!child.isForcedWin()
						&& (child.longestLosingWord().length() > longestLosingWord
								.length()))
					longestLosingWord = child.longestLosingWord();
			}
		}

		return longestLosingWord;
	}
} // end WordNode
